[CF708E]Student's Camp

2019-12-09
Codeforces

题意

有$(n+2)\cdot m$的矩阵,第2~n+1行最左的砖块和最右的砖块分别在白天和晚上有$P=\frac{A}{B}$的概率被毁

暴风雨持续了k天k夜,问k天之后整个矩阵仍然联通(共边)的概率,对1e9+7取模

题解

令$f_{T,l,r}$为第T行剩下l~r的砖块且上方全部联通的概率,$p_i$为第i块砖恰好留下的概率

得到一个naive的递推式

用前缀和优化, 令$sf_{T,j}=\sum_{1\leq i\leq j}f_{T,i,j}$,$ssf_{T,j}=\sum_{1\leq i\leq j}sf_{T,i}$

利用对称性翻转区间,$f_{T,x,y}=f_{T,m-y+1,m-x+1}$

又可以发现

直接维护sf和ssf就做完了 $\theta(n\cdot m)$

调试记录

初始化的时候fac和inv求得不够,应该求到k

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <cstdio>
#define LL long long
using namespace std;
const int maxn = 1505;
const LL mo = 1e9 + 7;
LL pow(LL x, LL t){
x %= mo; LL res = 1;
while (t > 0){
if (t & 1) res = 1ll * res * x % mo;
x = 1ll * x * x % mo;
t >>= 1;
}
return res;
}
int n, m, k, A, B;
LL P, p[maxn], sp[maxn], fac[maxn * 100], inv[maxn * 100];
LL C(int n, int m){
if (m == 0 || m == n) return 1;
if (m > n) return 0;
return fac[n] * inv[m] % mo * inv[n - m] % mo;
}
LL sf[maxn][maxn], ssf[maxn][maxn];
int main(){
scanf("%d%d%d%d%d", &n, &m, &A, &B, &k);
P = 1ll * A * pow(B, mo - 2) % mo; fac[0] = 1;
for (int i = 1; i <= k; i++) fac[i] = 1ll * fac[i - 1] * i % mo;
inv[k] = pow(fac[k], mo - 2); for (int i = k - 1; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % mo;
// printf("::%lld\n", C(5, 2));
for (int j = 0; j <= m; j++) p[j] = 1ll * pow(P, j) * pow(1 + mo - P, k - j) % mo * C(k, j) % mo;
sp[0] = p[0]; for (int j = 1; j <= m; j++) sp[j] = (sp[j - 1] + p[j]) % mo;
sf[0][m] = ssf[0][m] = 1;
for (int T = 1; T <= n; T++){
int S = 0;
for (int j = 1; j <= m; j++){
S = (S + 1ll * ssf[T - 1][j - 1] * p[j - 1] % mo) % mo;
sf[T][j] = (sf[T][j] + mo + 1ll * sp[j - 1] * p[m - j] % mo * (ssf[T - 1][m] - ssf[T - 1][m - j]) % mo) % mo;
sf[T][j] = (sf[T][j] + mo - 1ll * S * p[m - j] % mo) % mo;
ssf[T][j] = (ssf[T][j - 1] + sf[T][j]) % mo;
}
}
printf("%lld\n", ssf[n][m]);
return 0;
}